using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace WindowsPhoneGame1.Shapes
{
    public class Circle : Shape
    {
        public PointF Center { get; set; }
        public float Radius { get; set; }
        public override RectangleF Bounds { get; protected set; }

        public Circle(PointF center, float radius) : base()
        {
            Center = center;
            Radius = radius;
            SetBounds();
        }

        public override void SetBounds()
        {
            Bounds = new RectangleF(Center.X - Radius, Center.Y - Radius, 2 * Radius, 2 * Radius);
        }

        public ContainmentType Contains(Circle other)
        {
            float dx, dy;
            dx = other.Center.X - Center.X;
            dy = other.Center.Y - Center.Y;
            float distance = new Vector2(dx, dy).LengthSquared();
            float outsideTouchDistance = Radius + other.Radius;
            outsideTouchDistance *= outsideTouchDistance;

            if (distance > outsideTouchDistance)
                // other circle is outside this one
                return ContainmentType.Disjoint;

            float insideTouchDistance = Radius - other.Radius;
            insideTouchDistance *= insideTouchDistance;

            if (distance <= insideTouchDistance)
                // other circle is inside this one
                return ContainmentType.Contains;

            // if the other circle is not completely inside nor
            // outside, it intersects this circle
            return ContainmentType.Intersects;
        }

        /// <summary>
        /// Checks if a point is contained in this circle.
        /// Returns Disjoint if the point is outside,
        /// Returns Intersects if the point is on the circle edge,
        /// Returns Contains if the point is inside the circle
        /// </summary>
        /// <param name="other">The Point that may be contained in this circle</param>
        /// <returns> The ContainmentType of the shapes </returns>
        public ContainmentType Contains(PointF other)
        {
            float dx, dy;
            dx = other.X - Center.X;
            dy = other.Y - Center.Y;
            float distance = new Vector2(dx, dy).LengthSquared();

            float touchDistance = Radius * Radius;

            if (touchDistance > Radius)
                return ContainmentType.Disjoint;
            if (touchDistance < Radius)
                return ContainmentType.Contains;
            return ContainmentType.Intersects;
        }

        public ContainmentType Contains(Line other)
        {
            ContainmentType ta = Contains(other.A);
            ContainmentType tb = Contains(other.B);

            // the line is inside if both points are inside the circle or on the circle's edge
            if ((ta == ContainmentType.Intersects || ta == ContainmentType.Contains) &&
                (tb == ContainmentType.Intersects || tb == ContainmentType.Contains))
                return ContainmentType.Contains;

            // the line intersects if one point is inside the circle or on the circle's edge
            // and one point is outside the circle.
            if ((ta == ContainmentType.Contains || ta == ContainmentType.Intersects) &&
                tb == ContainmentType.Disjoint  ||
                (tb == ContainmentType.Contains || tb == ContainmentType.Intersects) &&
                ta == ContainmentType.Disjoint)
                return ContainmentType.Intersects;

            // if both points are outside the circle, part of the line may
            // still intersect.

            // translate the line as if the circle's radius is at (0, 0)
            other = new Line(
                new PointF(other.A.X - Center.X, other.A.Y - Center.Y),
                new PointF(other.B.X - Center.X, other.B.Y - Center.Y));

            float a, b, c, d;
            a = (other.B.X - other.A.X) * (other.B.X - other.A.X) +
                (other.B.Y - other.A.Y) * (other.B.Y - other.A.Y);
            b = 2 * (other.A.X * (other.B.X - other.A.X) + other.A.Y * (other.B.X - other.A.X));
            c = other.A.X * other.A.X + other.A.Y * other.A.Y - Radius * Radius;

            d = b * b - 4 * a * c;

            if (d >= 0)
                return ContainmentType.Intersects;

            return ContainmentType.Disjoint;
        }

        public override ContainmentType contains(Shape shape)
        {
            if(shape is PointF)
                return Contains((PointF)shape);
            if (shape is Line)
                return Contains((Line)shape);
            if (shape is Circle)
                return Contains((Circle)shape);
            throw new ArgumentException("Does not support collision with type " + shape.GetType());
        }
    }
}
